package com.propertree.mailer.publisher;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;

import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;

import com.google.common.collect.Lists;
import com.propertree.mailer.bean.EMailPartyInfo;
import com.propertree.mailer.bean.EmailAttributes;
import com.propertree.mailer.bean.EmailAttributesConfiguration;
import com.propertree.mailer.bean.MailPublisherConfiguration;
import com.propertree.mailer.bean.SenderReceiverConfig;
import com.propertree.mailer.config.EmailAttributeHelper;
import com.propertree.mailer.config.EmailServiceProviderConfiguration;
import com.propertree.mailer.config.MailerConfigContainer;
import com.propertree.mailer.helper.MessageDecorator;
import com.propertree.mailer.helper.SessionHelper;
import com.propertree.mailer.manager.ExcelSenderReceiverInfo;
import com.propertree.mailer.manager.MailerSenderReceiverInfo;

public class MailPublisher implements Callable<Boolean> {
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");
	private static final String EXT_TXT = "txt";
	private static final String EXT_XLS = "xls";

	@Override
	public Boolean call() throws Exception {
		try {
			execute();
		} catch (Exception e) {
			e.printStackTrace();
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}

	private void execute() {
		MailerConfigContainer mailerConfigContainer = MailerConfigContainer.getInstance();
		// Validation SenderReceiverConfig
		SenderReceiverConfig senderReceiverConfig = validateMailerConfiguration(mailerConfigContainer);

		// Validate SenderReceiverConfig
		EmailAttributesConfiguration emailAttribConfig = validateEmailAttributes(mailerConfigContainer);
		// Create EmailAttributes
		EmailAttributes emailAttributes = EmailAttributeHelper.createEmailAttribute(emailAttribConfig);

		MailPublisherConfiguration publisherConfiguration = mailerConfigContainer.getMailPubConfig();

		Properties props = EmailServiceProviderConfiguration.createServiceProviderProperties();
		MailerSenderReceiverInfo info = new ExcelSenderReceiverInfo();
		List<EMailPartyInfo> sendersInfoList = info.getSendersInfo(senderReceiverConfig);
		List<EMailPartyInfo> receiversInfoList = info.getReceiversInfo(senderReceiverConfig);
		List<String> outputDataList = new ArrayList<String>(receiversInfoList.size());

		// Sending Emails
		sendMail(props, sendersInfoList, receiversInfoList, outputDataList, emailAttributes, publisherConfiguration);
	}

	private EmailAttributesConfiguration validateEmailAttributes(MailerConfigContainer mailerConfigContainer) {
		EmailAttributesConfiguration emailAttributesConfiguration = mailerConfigContainer.getEmailAttribConfig();
		String mailSubFilePath = mailerConfigContainer.getEmailAttribConfig().getEmailSubjectFile();
		validateFilePath(mailSubFilePath, EXT_TXT);
		emailAttributesConfiguration.setEmailSubjectFile(mailSubFilePath);
		String mailBodyFilePath = mailerConfigContainer.getEmailAttribConfig().getEmailBodyFile();
		validateFilePath(mailSubFilePath, EXT_TXT);
		emailAttributesConfiguration.setEmailBodyFile(mailBodyFilePath);
		String outputFile = mailerConfigContainer.getMailPubConfig().getOutputFilePath();
		validateFilePath(outputFile, EXT_TXT);

		return emailAttributesConfiguration;
	}

	private SenderReceiverConfig validateMailerConfiguration(MailerConfigContainer mailerConfigContainer) {
		SenderReceiverConfig config = mailerConfigContainer.getSenderReceiverConfig();
		String senderFilePath = mailerConfigContainer.getSenderReceiverConfig().getSenderFilePath();
		validateFilePath(senderFilePath, EXT_XLS);
		// config.setSenderFilePath(senderFilePath);

		String receiverFilePath = mailerConfigContainer.getSenderReceiverConfig().getReceiverFilePath();
		validateFilePath(receiverFilePath, EXT_XLS);
		// config.setReceiverFilePath(receiverFilePath);

		return config;
	}

	private void sendMail(Properties props, List<EMailPartyInfo> senders, List<EMailPartyInfo> receiversInfo,
			List<String> outputData, EmailAttributes emailAttributes, MailPublisherConfiguration publisherConfiguration) {
		String senderReceiverTuple = "";
		String outputFile = publisherConfiguration.getOutputFilePath();

		try {
			List<List<EMailPartyInfo>> receiverPartyList = getPartyBlock(receiversInfo,
					publisherConfiguration.getBlockSize());
			int totalReceivers = receiverPartyList.size();
			int counter = 0;
			Thread.sleep(publisherConfiguration.getDelayedStartInMillis());

			for (EMailPartyInfo senderParty : senders) {
				Session session = null;
				boolean isSenderErr = false;
				try {
					session = SessionHelper.createSession(props, senderParty);
				} catch (Exception e) {
					isSenderErr = true;
					outputData.add("Sender Error :: " + senderParty.getEmailId() + LINE_SEPARATOR);
					continue;
				}
				List<EMailPartyInfo> receivers = null;
				if (counter < totalReceivers) {
					receivers = receiverPartyList.get(counter++);
				}
				if (receivers == null) {
					break;
				}
				for (EMailPartyInfo receiverParty : receivers) {
					senderReceiverTuple = "From :" + senderParty.getEmailId() + " To :" + receiverParty.getEmailId();
					Message message = null;
					boolean isSendingErr = false;
					try {
						message = MessageDecorator.createMessage(session, senderParty, receiverParty, emailAttributes);
					} catch (Exception e) {
						isSendingErr = true;
						outputData.add("Error :: " + senderReceiverTuple + LINE_SEPARATOR);
					}
					try {
						Transport.send(message);
					} catch (Exception e) {
						System.out.println("Got error :: " + e.getMessage());
						isSendingErr = true;
						outputData.add("Error :: " + senderReceiverTuple + LINE_SEPARATOR);
					}
					if (!isSendingErr) {
						outputData.add("Email Sent ::" + senderReceiverTuple + LINE_SEPARATOR);
					}
					Thread.sleep(publisherConfiguration.getPausePerMessageInMillis());
				}
				/*
				 * if(!isSenderErr){ counter = counter + 1; }
				 */

				session = null;
				Thread.sleep(publisherConfiguration.getPausePerBlockInMiilis());
			}
		} catch (Exception e) {
			outputData.add("Error :: " + senderReceiverTuple);
		} finally {
			try {
				BufferedWriter bf = new BufferedWriter(new FileWriter(new File(outputFile + "_"
						+ System.currentTimeMillis())));
				for (String data : outputData) {
					bf.write(data);
					bf.flush();
				}
				bf.close();
			} catch (IOException e) {
				System.out.println("Error while writing outputfile. Below emails have been sent.");
				System.out.println(outputData);
			}
		}
	}

	private static List<List<EMailPartyInfo>> getPartyBlock(List<EMailPartyInfo> receivers, int blockSize) {
		return Lists.partition(receivers, blockSize);
	}

	private static void validateFilePath(String filePath, String ext) {
		if (filePath == null || filePath.trim().equals("")) {
			System.out.println("One more more file paths are not given. Please check the path properly.");
			System.exit(-1);
		}

		File file = new File(filePath);
		if (!file.exists()) {
			System.out.println("File :: " + filePath + " doesn't exist. Please check the path properly.");
			System.exit(-1);
		}

		if (!file.canRead()) {
			System.out.println("File :: " + filePath + " can't be read. Please check file permission.");
			System.exit(-1);
		}

		if (!file.isFile()) {
			System.out.println("File :: " + filePath + " seems a directory. Please check properly.");
			System.exit(-1);
		}
		String fileName = file.getName();
		int dot = fileName.lastIndexOf('.');
		if (dot <= 0) {
			System.out.println("File :: " + filePath
					+ " doesn't seem to have proper extension. Check it is 'xls' file.");
			System.exit(-1);
		}
		String extension = (dot == -1) ? "" : fileName.substring(dot + 1);

		if (!ext.equalsIgnoreCase(extension)) {
			System.out.println("File :: " + filePath + " doesn't seem to have proper extension. Check it is '" + ext
					+ "' file.");
			System.exit(-1);
		}
	}

	private static int getValidField(String propertyName, String blockSizeStr) {
		if (blockSizeStr == null || blockSizeStr.trim().equals("")) {
			System.out.println(propertyName + " is not proper.");
			System.exit(-1);
		}
		Integer blockSize = 0;
		try {
			blockSize = Integer.parseInt(blockSizeStr);

		} catch (Exception e) {
			System.out.println(propertyName + " is not proper.");
			System.exit(-1);
		}

		return blockSize;
	}
}